/*******************************************************************************
 ** File name           ：Wiegand.h
 ** Last update date    ：2021/5/19
 ** Author              ：licl
 ** Usage               ：用于韦根协议数据解析发送
 ********************************************************************************
*/
/*
  Release ：
  v0.1 支持：Wiegand26位34位数据的接受与发送。
 */

#include "Wiegand.h"
#include <stdio.h>
#include <string.h>

#include "arm_compat.h"

#define ENABLE_INT __enable_irq
#define DISABLE_INT __disable_irq

int send_data(Wiegand_Valid_data_S out_data);
Wiegand_Valid_data_S get_data(void);
void check_received_over(void);

Wiegand_S user_Wiegand;

int init_wiegand(
    Wiegand_S *wiegand,
    HardwareInit wiegand_in,
    HardwareInit wiegand_out,
    wiegand_delay_us delay_us,
    wiegand_delay_ms delay_ms,
    wiegand_timer_start_ms timer_start,
    wiegand_timer_stop timer_stop,
    wiegand_gpio_output gpio_0_out0,
    wiegand_gpio_output gpio_0_out1,
    wiegand_gpio_output gpio_1_out0,
    wiegand_gpio_output gpio_1_out1)
{
  wiegand->input_gpio_int = wiegand_in;
  wiegand->output_gpio_int = wiegand_out;
  wiegand->delay_us = delay_us;
  wiegand->delay_ms = delay_ms;
  wiegand->timer_start_ms = timer_start;
  wiegand->timer_stop = timer_stop;
  wiegand->input_data_0 = wiegand_input_0;
  wiegand->input_data_1 = wiegand_input_1;
  wiegand->data_received_done = check_received_over;

  wiegand->send_data = send_data;
  wiegand->d0_output0 = gpio_0_out0;
  wiegand->d0_output1 = gpio_0_out1;
  wiegand->d1_output0 = gpio_1_out0;
  wiegand->d1_output1 = gpio_1_out1;

  wiegand->get_data = get_data;

  wiegand->input_gpio_int();

  return 0;
}

// 0 ok  -1 data overflow
int wiegand_input_1(Wiegand_S *wiegand)
{
  uint8_t hsb, lsb = 0;

#if 0
		if(wiegand->WiegandReceiveData[wiegand->write].writeBitcnt>=2)
		{
			hsb=(wiegand->WiegandReceiveData[wiegand->write].writeBitcnt-2)/8;
			lsb=(wiegand->WiegandReceiveData[wiegand->write].writeBitcnt-2)%8;
			lsb=7-lsb;
			if(hsb > 5 )  // 接受数据的data length最大40;
			return -1;
			wiegand->WiegandReceiveData[wiegand->write].data[hsb] |= (0x01<<lsb);
		}

#else
  //根据结构体里面的计数来填充buff里面的0和1

  hsb = wiegand->WiegandReceiveData[wiegand->write].writeBitcnt / 8;
  lsb = wiegand->WiegandReceiveData[wiegand->write].writeBitcnt % 8;
  lsb = 7 - lsb;
  if (hsb > 5) // 接受数据的data length最大40;
    return -1;
  wiegand->WiegandReceiveData[wiegand->write].data[hsb] |= (0x01 << lsb);

#endif
  wiegand->WiegandReceiveData[wiegand->write].writeBitcnt++;
  wiegand->timer_start_ms();
  return 0;
}
int wiegand_input_0(Wiegand_S *wiegand)
{
  //根据结构体里面的计数来填充buff里面的0和1
  uint8_t hsb, lsb = 0;
#if 0
			if(wiegand->WiegandReceiveData[wiegand->write].writeBitcnt>=2)
			{
				hsb=(wiegand->WiegandReceiveData[wiegand->write].writeBitcnt-2)/8;
			  lsb=(wiegand->WiegandReceiveData[wiegand->write].writeBitcnt-2)%8;
				lsb=7-lsb;
				if(hsb > 5 )  // 接受数据的data length最大40;
				return -1;
			  wiegand->WiegandReceiveData[wiegand->write].data[hsb] &= (~(0x01<<lsb));
			}
#else
  hsb = wiegand->WiegandReceiveData[wiegand->write].writeBitcnt / 8;
  lsb = wiegand->WiegandReceiveData[wiegand->write].writeBitcnt % 8;
  lsb = 7 - lsb;
  if (hsb > 5) // 接受数据的data length最大40;
    return -1;
  wiegand->WiegandReceiveData[wiegand->write].data[hsb] &= (~(0x01 << lsb));
#endif
  wiegand->WiegandReceiveData[wiegand->write].writeBitcnt++;
  wiegand->timer_start_ms();
  return 0;
}
/*
Need a callback function to be executed after about 200ms 
to determine whether the data has been accepted .
*/
void check_received_over(Wiegand_S *wiegand, )
{
  uint8_t i = 0;
  wiegand->WiegandReceiveData[wiegand->write].flag = USED;
  //wiegand->WiegandReceiveData[wiegand->write].writeBitcnt -=2;
  for (; i < WiegandReceiveDataBuffcnt; i++)
  {
    if (wiegand->WiegandReceiveData[i].flag == UNUSED)
      break;
  }
  wiegand->timer_stop();
  if (i >= WiegandReceiveDataBuffcnt)
    return; //receive Data overflow
  wiegand->write = i;
  return;
}

//check Wiegand26
//0 ok ,-1 check error ,-2 ptr null
int wiegand26_check(uint8_t use, Wiegand_Valid_data_S *weigan_temp, Wiegand_S *wiegand)
{
  uint8_t odd_cnt = 0, parity_cnt = 0;
  uint16_t hid_temp = 0, pid_temp = 0;
  if (weigan_temp == NULL)
  {
    return -2;
  }

  for (uint8_t i = 0; i < 13; i++)
  {
    if (wiegand->WiegandReceiveData[use].data[i / 8] & ((0x01 << (7 - (i % 8)))))
      parity_cnt++;
  }
  if (parity_cnt % 2 != 0)
    return -1;

  for (uint8_t i = 13; i < 26; i++)
  {
    if (wiegand->WiegandReceiveData[use].data[i / 8] & ((0x01 << (7 - (i % 8)))))
      odd_cnt++;
  }
  if (odd_cnt % 2 == 0)
    return -1;

  //校验通过

  weigan_temp->dataType = Wiegand26;

  for (uint8_t i = 1; i < 9; i++)
  {
    if (wiegand->WiegandReceiveData[use].data[i / 8] & ((0x01 << (7 - (i % 8)))))
    {
      hid_temp |= (0x01 << (8 - i));
    }
  }

  for (uint8_t i = 9; i < 25; i++)
  {
    if (wiegand->WiegandReceiveData[use].data[i / 8] & ((0x01 << (7 - (i % 8)))))
    {
      pid_temp |= (0x01 << (24 - i));
    }
  }

  weigan_temp->hid = hid_temp;
  weigan_temp->pid = pid_temp;

  return 0;
}
//check Wiegand34
//0 ok ,-1 check error ,-2 ptr null
int wiegand34_check(uint8_t use, Wiegand_Valid_data_S *weigan_temp, Wiegand_S *wiegand)
{
  uint8_t odd_cnt = 0, parity_cnt = 0;
  uint16_t hid_temp = 0, pid_temp = 0;
  if (weigan_temp == NULL)
  {
    return -2;
  }

  for (uint8_t i = 0; i < 17; i++)
  {
    if (wiegand->WiegandReceiveData[use].data[i / 8] & ((0x01 << (7 - (i % 8)))))
      parity_cnt++;
  }
  if (parity_cnt % 2 != 0)
    return -1;

  for (uint8_t i = 17; i < 34; i++)
  {
    if (wiegand->WiegandReceiveData[use].data[i / 8] & ((0x01 << (7 - (i % 8)))))
      odd_cnt++;
  }
  if (odd_cnt % 2 == 0)
    return -1;

  //校验通过

  weigan_temp->dataType = Wiegand34;

  for (uint8_t i = 1; i < 17; i++)
  {
    if (wiegand->WiegandReceiveData[use].data[i / 8] & ((0x01 << (7 - (i % 8)))))
    {
      hid_temp |= (0x01 << (16 - i));
    }
  }

  for (uint8_t i = 17; i < 33; i++)
  {
    if (wiegand->WiegandReceiveData[use].data[i / 8] & ((0x01 << (7 - (i % 8)))))
    {
      pid_temp |= (0x01 << (32 - i));
    }
  }

  weigan_temp->hid = hid_temp;
  weigan_temp->pid = pid_temp;

  return 0;
}

Wiegand_Valid_data_S get_data(Wiegand_S *wiegand)
{
  uint8_t i = 0;
  Wiegand_Valid_data_S wiegand_temp;

  wiegand_temp.dataType = WiegandNone;

  for (; i < WiegandReceiveDataBuffcnt; i++)
  {
    if (wiegand->WiegandReceiveData[i].flag == USED)
      break;
  }
  if (i >= WiegandReceiveDataBuffcnt)
  {
    return wiegand_temp;
  }
  switch (wiegand->WiegandReceiveData[i].writeBitcnt)
  {
  case 26:
    wiegand26_check(i, &wiegand_temp);

    break;
  case 34:

    wiegand34_check(i, &wiegand_temp);

    break;

  default:
    // do other thing

    break;
  }

  wiegand->WiegandReceiveData[i].flag = UNUSED;
  wiegand->WiegandReceiveData[i].writeBitcnt = 0;
  return wiegand_temp;
}

WiegandData_S wiegand_data_calculate_check(Wiegand_Valid_data_S out_data)
{
  WiegandData_S wiegan_data_temp;
  uint64_t weigand34_temp = 0;
  uint16_t hid_temp = 0, pid_temp = 0;

  uint32_t temp = 0;

  uint8_t cnt = 0;
  memset(&wiegan_data_temp, 0, sizeof(wiegan_data_temp));
  switch (out_data.dataType)
  {
  case Wiegand26:

    wiegan_data_temp.writeBitcnt = 26;
#if 0
			hid_temp = out_data.hid%256;
			pid_temp = out_data.pid;
			hid_temp = (hid_temp << 4) + (pid_temp >> 12);
			for(uint8_t i = 0; i < 12; i++)
			{
				if(hid_temp & (0x01 << i))
					cnt++;
			}
			if(cnt%2 != 0)
			wiegan_data_temp.data[0]=	0x80;
				
				
			wiegan_data_temp.data[0] |= (out_data.hid%256) >> 1;
			wiegan_data_temp.data[1]  = ((out_data.hid%256) & 0x01) << 7;
			wiegan_data_temp.data[1] |= (out_data.pid >> 9);
			wiegan_data_temp.data[2]  = (out_data.pid & 0x01FF) >>1;
			wiegan_data_temp.data[3]  = (out_data.pid & 0x01) << 7;
				
			cnt = 0;
			pid_temp = pid_temp & 0x03ff;
				
			for(uint8_t i=0 ; i < 12; i++)
			{
					if(pid_temp & (0x01 << i))
					cnt++;
			}
			
			if(cnt%2 == 0)
			wiegan_data_temp.data[3] |=	0x01 << 6;
#else

    temp = out_data.hid % 256;
    temp = temp << 23;

    temp |= out_data.pid << 7;

    for (uint8_t i = 1; i < 15; i++)
    {
      if (temp & (0x80000000 >> i))
        cnt++;
    }
    if (cnt % 2 != 0)
      temp |= 0x80000000;

    for (uint8_t i = 15; i < 26; i++)
    {
      if (temp & (0x80000000 >> i))
        cnt++;
    }
    if (cnt % 2 == 0)
      temp |= 0x00000080;

    wiegan_data_temp.data[0] = temp >> 24;
    wiegan_data_temp.data[1] = temp >> 16;
    wiegan_data_temp.data[2] = temp >> 8;
    wiegan_data_temp.data[3] = temp;

#endif

    break;

  case Wiegand34:
    wiegan_data_temp.writeBitcnt = 34;
    weigand34_temp = out_data.hid;
    weigand34_temp <<= 47;

    weigand34_temp |= ((uint64_t)out_data.pid) << 31;

    for (uint8_t i = 1; i < 17; i++)
    {
      if (weigand34_temp & (0x8000000000000000 >> i))
        cnt++;
    }
    if (cnt % 2 != 0)
    {
      weigand34_temp |= 0x8000000000000000;
    }

    for (uint8_t i = 17; i < 33; i++)
    {
      if (weigand34_temp & (0x8000000000000000 >> i))
        cnt++;
    }
    if (cnt % 2 == 0)
    {
      weigand34_temp |= 0x0000000040000000;
    }
    wiegan_data_temp.data[0] = weigand34_temp >> 56;
    wiegan_data_temp.data[1] = (weigand34_temp << 8) >> 56;
    wiegan_data_temp.data[2] = (weigand34_temp << 16) >> 56;
    wiegan_data_temp.data[3] = (weigand34_temp << 24) >> 56;
    wiegan_data_temp.data[4] = (weigand34_temp << 32) >> 56;

    break;
  }
  return wiegan_data_temp;
}

int output_0(Wiegand_S *wiegand)
{

  wiegand->d0_output0();
  wiegand->delay_us(DATA_HOLD_TIME);
  wiegand->d0_output1();
  wiegand->delay_ms(DATA_INTERVAL_TIME);
  return 0;
}

int output_1(Wiegand_S *wiegand)
{
  wiegand->d1_output0();
  wiegand->delay_us(DATA_HOLD_TIME);
  wiegand->d1_output1();
  wiegand->delay_ms(DATA_INTERVAL_TIME);
  return 0;
}

int send_data(Wiegand_Valid_data_S out_data, Wiegand_S *wiegand)
{
  WiegandData_S wiegan_data_temp;
  wiegan_data_temp = wiegand_data_calculate_check(out_data);

  wiegand->output_gpio_int();

  DISABLE_INT();
  for (uint8_t i = 0; i < wiegan_data_temp.writeBitcnt; i++)
  {
    if (wiegan_data_temp.data[i / 8] & ((0x01 << (7 - (i % 8)))))
    {
      output_1();
    }
    else
    {
      output_0();
    }
  }
  wiegand->input_gpio_int();

  ENABLE_INT();

  return 0;
}
